package com.tang.plugin;

import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ThrowableRunnable;
import com.tang.plugin.model.FileTreeNode;
import com.tang.plugin.model.Module;
import com.tang.plugin.utils.NotificationUtils;
import com.tang.plugin.utils.PluginKit;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class FileWriteCommand implements ThrowableRunnable<Exception> {

    private final PluginKit kit;
    private final Module module;

    public FileWriteCommand(PluginKit kit, Module module) {
        this.kit = kit;
        this.module = module;
    }

    public static void startAction(PluginKit kit, Module module) {
        try {
            WriteCommandAction.writeCommandAction(kit.getProject())
                    .withGlobalUndo()
                    .withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION)
                    .run(new FileWriteCommand(kit, module));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() throws Exception {
        VirtualFile current = kit.getVirtualFile();
        if (current == null || !current.isDirectory()) {
            PluginKit.showErrorMessage("当前目标不是目录");
            return;
        }
        FileTreeNode node = module.getTemplate();
        node.build();

        List<FileTreeNode> failedList = new ArrayList<>();
        node.getChildren().forEach((it) -> failedList.addAll(createFileTree(it, current)));

        createFileInLibrary();

        if (!failedList.isEmpty()) {
            StringBuilder builder = new StringBuilder();
            failedList.forEach((it) -> {
                builder.append(it.getRealName());
                builder.append("\n");
            });
            NotificationUtils.showError(builder.toString(), "以上文件创建失败");
        }
    }

    private List<FileTreeNode> createFileTree(FileTreeNode treeNode, VirtualFile currentDirectory) {
        List<FileTreeNode> failedList = new ArrayList<>();
        if (treeNode.isDir()) {
            VirtualFile dir = kit.createDir(treeNode.getRealName(), currentDirectory);
            if (dir == null) {
                failedList.add(treeNode);
            } else {
                treeNode.getChildren().forEach((it) -> failedList.addAll(createFileTree(it, dir)));
            }
        } else {
            String fileTemplate = treeNode.getTemplateFile();
            if (fileTemplate != null && fileTemplate.length() != 0) {
                Map<String, String> placeHolderInherit = treeNode.getPlaceHolderInherit();
                if (placeHolderInherit == null) {
                    placeHolderInherit = new HashMap<>();
                }
                String moduleName = placeHolderInherit.get("MODULE_NAME");
                placeHolderInherit.put("UPPER_MODULE_NAME", moduleName.toUpperCase());
                PsiElement result = kit.createFileFromTemplate(treeNode.getRealName(), fileTemplate,
                        placeHolderInherit, currentDirectory);
                if (result == null || !result.isValid()) {
                    failedList.add(treeNode);
                }
            } else {
                if (!kit.createFile(treeNode.getRealName(), currentDirectory)) {
                    failedList.add(treeNode);
                }
            }
        }
        return failedList;
    }

    /**
     * 在libBaseRes模块插入代码
     */
    private void createFileInLibrary() {
        VirtualFile current = kit.getVirtualFile();
        Map<String, String> placeholder = module.getTemplate().getPlaceHolderInherit();
        if (placeholder == null) {
            placeholder = new HashMap<>();
        }

        String moduleName = placeholder.get("MODULE_NAME");
        placeholder.put("UPPER_MODULE_NAME", moduleName.toUpperCase());

        VirtualFile managerDir = current.findFileByRelativePath("../Librarys/libBaseRes/src/main/java/com/sandboxol/center/router/manager");
        if (managerDir != null) {
            kit.createFileFromTemplate("${MODULE_NAME}Manager", "Template ModuleManager.java", placeholder, managerDir);
        }
        VirtualFile moduleApiDir = current.findFileByRelativePath("../Librarys/libBaseRes/src/main/java/com/sandboxol/center/router/moduleApi");
        if (moduleApiDir != null) {
            kit.createFileFromTemplate("I${MODULE_NAME}Service", "Template IModuleService.java", placeholder, moduleApiDir);
        }

        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(kit.getProject());
        PsiType psiType = PsiType.getTypeByName("java.lang.String", kit.getProject(), GlobalSearchScope.EMPTY_SCOPE);

        VirtualFile moduleApplicationDir = current.findFileByRelativePath("../Librarys/libBaseRes/src/main/java/com/sandboxol/center/router/moduleApplication");
        if (moduleApplicationDir != null) {
            VirtualFile[] children = moduleApplicationDir.getChildren();
            if (children == null) {
                return;
            }
            for (VirtualFile vf : children) {
                if (!vf.isDirectory() && vf.getName().equals("ModuleReflexs.java")) {
                    PsiFile psiFile = PsiManager.getInstance(kit.getProject()).findFile(vf);
                    PsiJavaFile psiJavaFile = (PsiJavaFile) psiFile;
                    final PsiClass[] classes = psiJavaFile.getClasses();
                    if (moduleName != null) {
                        String serviceName = moduleName.toUpperCase() + "_SERVICE_APP";
                        PsiField serviceApp = classes[0].findFieldByName(serviceName, false);
                        if (serviceApp == null) {
                            serviceApp = elementFactory.createField(serviceName, psiType);
                            PsiUtil.setModifierProperty(serviceApp, PsiModifier.PRIVATE, true);
                            PsiUtil.setModifierProperty(serviceApp, PsiModifier.STATIC, true);
                            PsiUtil.setModifierProperty(serviceApp, PsiModifier.FINAL, true);

                            String packageName = placeholder.get("PACKAGE_NAME");
                            String featureName = placeholder.get("FEATURE_NAME");

                            PsiExpression psiExpression = elementFactory.createExpressionFromText("\"" + packageName + "." + featureName + "." + moduleName + "App\"", null);
                            serviceApp.setInitializer(psiExpression);
                            classes[0].add(serviceApp);
                        }

                        String initModuleNames = "initModuleNames";
                        PsiField initModuleArray = classes[0].findFieldByName(initModuleNames, false);
                        if (initModuleArray != null) {
                            PsiField copy = (PsiField) initModuleArray.copy();
                            initModuleArray.delete();
                            PsiExpression initializer = copy.getInitializer();
                            String text = initializer.getText();
                            String replace = text.replace("}", ",\n            " + serviceName + "}");
                            PsiExpression psiExpression = elementFactory.createExpressionFromText(replace, null);
                            copy.setInitializer(psiExpression);
                            classes[0].add(copy);
                        }
                    }
                }
            }
        }

        VirtualFile pathDir = current.findFileByRelativePath("../Librarys/libBaseRes/src/main/java/com/sandboxol/center/router/path");
        if (pathDir != null) {
            VirtualFile[] children = pathDir.getChildren();
            if (children == null) {
                return;
            }
            for (VirtualFile vf : children) {
                if (!vf.isDirectory() && vf.getName().equals("RouterServicePath.java")) {
                    PsiFile psiFile = PsiManager.getInstance(kit.getProject()).findFile(vf);
                    PsiJavaFile psiJavaFile = (PsiJavaFile) psiFile;
                    final PsiClass[] classes = psiJavaFile.getClasses();
                    if (moduleName != null) {
                        PsiClass psiClass = classes[0].findInnerClassByName(moduleName, false);
                        if (psiClass == null) {
                            PsiField base = elementFactory.createField("BASE", psiType);
                            PsiUtil.setModifierProperty(base, PsiModifier.PRIVATE, true);
                            PsiUtil.setModifierProperty(base, PsiModifier.STATIC, true);
                            PsiUtil.setModifierProperty(base, PsiModifier.FINAL, true);
                            PsiExpression baseExpression = elementFactory.createExpressionFromText("\"/" + moduleName + "\"", null);
                            base.setInitializer(baseExpression);

                            PsiField serviceName = elementFactory.createField(moduleName.toUpperCase(), psiType);
                            PsiUtil.setModifierProperty(serviceName, PsiModifier.PUBLIC, true);
                            PsiUtil.setModifierProperty(serviceName, PsiModifier.STATIC, true);
                            PsiUtil.setModifierProperty(serviceName, PsiModifier.FINAL, true);
                            PsiExpression serviceExpression = elementFactory.createExpressionFromText("BASE + \"/service\"", null);
                            serviceName.setInitializer(serviceExpression);

                            String baseText = base.getText();
                            String serviceNameText = serviceName.getText();

                            psiClass = elementFactory.createClassFromText(baseText + "\n" + serviceNameText, null);
                            psiClass.setName(moduleName);
                            PsiUtil.setModifierProperty(psiClass, PsiModifier.PUBLIC, true);
                            PsiUtil.setModifierProperty(psiClass, PsiModifier.STATIC, true);

                            classes[0].add(psiClass);
                        }
                    }
                }
            }
        }
    }

}
